#include <iostream>
#include <random>
#include <iomanip>

#include "../code/CachePolicyIface.h"
#include "../code/lru/LruCache.hpp"
#include "../code/lfu/LfuCache.hpp"
#include "../code/arc/ArcCache.hpp"

// 未用
class Timer
{
public:
    Timer() : start_(std::chrono::high_resolution_clock::now()) {}

    double elapsed()
    {
        auto now = std::chrono::high_resolution_clock::now();
        return std::chrono::duration_cast<std::chrono::milliseconds>(now - this->start_).count();
    }

private:
    std::chrono::time_point<std::chrono::high_resolution_clock> start_;
};

// 辅助函数：打印结果
void printResults(const std::string &testName,
                  int capacity,
                  const std::vector<int> &getOperations,
                  const std::vector<int> &hits)
{
    std::cout << "缓存大小: " << capacity << std::endl;
    std::cout << "LRU - 命中率: " << std::fixed << std::setprecision(2)
              << (100.0 * hits[0] / getOperations[0]) << "%" << std::endl;
    std::cout << "LFU - 命中率: " << std::fixed << std::setprecision(2)
              << (100.0 * hits[1] / getOperations[1]) << "%" << std::endl;
    std::cout << "ARC - 命中率: " << std::fixed << std::setprecision(2)
              << (100.0 * hits[2] / getOperations[2]) << "%" << std::endl;
}

void testHotDataAccess()
{
    std::cout << "\n=== 测试场景1: 热点数据访问测试 ===" << std::endl;

    const int CAPACITY = 50;          // 增加缓存容量
    const int OPERATIONS = 500000;    // 增加操作次数
    const int HOT_KEYS_Range = 20;    // 增加热点数据的数量。热点数据范围[0,19]
    const int COLD_KEYS_Range = 5000; // 冷数据范围[20,5019]

    // std::array<> 要求元素可拷贝
    std::shared_ptr<CachePool::LruCache<int, std::string>> lru = std::make_shared<CachePool::LruCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::LfuCache<int, std::string>> lfu = std::make_shared<CachePool::LfuCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::ArcCache<int, std::string>> arc = std::make_shared<CachePool::ArcCache<int, std::string>>(CAPACITY);

    std::array<std::shared_ptr<CachePool::CachePolicyIface<int, std::string>>, 3> caches{lru, lfu, arc};

    std::random_device rd{};
    std::mt19937 genRandomNum(rd());

    std::vector<int> getOperations(3, 0); // 每缓存get操作次数
    std::vector<int> hits(3, 0);          // 每缓存命中次数

    // 先进行一系列set操作
    for (int i = 0; i < caches.size(); ++i)
    {
        for (int setOp = 0; setOp < OPERATIONS; ++setOp)
        {
            int key{};
            if (setOp % 100 < 70) // 70%热点数据
            {
                key = genRandomNum() % HOT_KEYS_Range;
            }
            else // 30%冷数据
            {
                key = HOT_KEYS_Range + (genRandomNum() % COLD_KEYS_Range);
            }

            std::string value = "value" + std::to_string(key);

            caches[i]->set(key, value);
        }

        // 然后进行随机get操作
        for (int getOp = 0; getOp < OPERATIONS; ++getOp)
        {
            int key{};
            if (getOp % 100 < 70) // 70%概率访问热点
            {
                key = genRandomNum() % HOT_KEYS_Range;
            }
            else // 30%概率访问冷数据
            {
                key = HOT_KEYS_Range + (genRandomNum() % COLD_KEYS_Range);
            }

            ++getOperations[i];

            std::string result{};
            if (caches[i]->get(key, result))
            {
                ++hits[i];
            }
        }
    }

    printResults("热点数据访问测试", CAPACITY, getOperations, hits);
}

void testCyclicScan()
{
    std::cout << "\n=== 测试场景2: 循环扫描测试 ===" << std::endl;

    const int CAPACITY = 50; // 增加缓存容量
    const int CYCLE_SIZE = 500;
    const int OPERATIONS = 200000; // 增加操作次数

    // std::array<> 要求元素可拷贝
    std::shared_ptr<CachePool::LruCache<int, std::string>> lru = std::make_shared<CachePool::LruCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::LfuCache<int, std::string>> lfu = std::make_shared<CachePool::LfuCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::ArcCache<int, std::string>> arc = std::make_shared<CachePool::ArcCache<int, std::string>>(CAPACITY);

    std::array<std::shared_ptr<CachePool::CachePolicyIface<int, std::string>>, 3> caches{lru, lfu, arc};

    std::random_device rd{};
    std::mt19937 genRandomNum(rd());

    std::vector<int> getOperations(3, 0); // 每缓存get操作次数
    std::vector<int> hits(3, 0);          // 每缓存命中次数

    // 先填充数据
    for (int i = 0; i < caches.size(); ++i)
    {
        // 只填充 CYCLE_SIZE 的数据
        for (int key = 0; key < CYCLE_SIZE; ++key)
        {
            std::string value = "cycle" + std::to_string(key);

            caches[i]->set(key, value);
        }

        // 然后进行访问测试
        int curPos = 0;
        for (int getOp = 0; getOp < OPERATIONS; ++getOp)
        {
            int key{};
            if (getOp % 100 < 60) // 60%顺序扫描
            {
                key = curPos;
                curPos = (curPos + 1) % CYCLE_SIZE;
            }
            else if (getOp % 100 < 90) // 30%随机跳跃
            {
                key = genRandomNum() % CYCLE_SIZE;
            }
            else // 10%访问范围外数据
            {
                key = CYCLE_SIZE + (genRandomNum() % CYCLE_SIZE);
            }

            ++getOperations[i];

            std::string result{};
            if (caches[i]->get(key, result))
            {
                ++hits[i];
            }
        }
    }

    printResults("循环扫描测试", CAPACITY, getOperations, hits);
}

void testWorkLoadShift()
{
    std::cout << "\n=== 测试场景3: 工作负载剧烈变化测试 ===" << std::endl;

    const int CAPACITY = 4;
    const int OPERATIONS = 80000;
    const int PHASE_LENGTH = OPERATIONS / 5;

    // std::array<> 要求元素可拷贝
    std::shared_ptr<CachePool::LruCache<int, std::string>> lru = std::make_shared<CachePool::LruCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::LfuCache<int, std::string>> lfu = std::make_shared<CachePool::LfuCache<int, std::string>>(CAPACITY);
    std::shared_ptr<CachePool::ArcCache<int, std::string>> arc = std::make_shared<CachePool::ArcCache<int, std::string>>(CAPACITY);

    std::array<std::shared_ptr<CachePool::CachePolicyIface<int, std::string>>, 3> caches{lru, lfu, arc};

    std::random_device rd{};
    std::mt19937 genRandomNum(rd());

    std::vector<int> getOperations(3, 0); // 每缓存get操作次数
    std::vector<int> hits(3, 0);          // 每缓存命中次数

    // 先填充一些初始数据
    for (int i = 0; i < caches.size(); ++i)
    {
        for (int key = 0; key < 1000; ++key)
        {
            std::string value = "init" + std::to_string(key);

            caches[i]->set(key, value);
        }

        // 然后进行多阶段测试
        for (int getOp = 0; getOp < OPERATIONS; ++getOp)
        {
            int key{};
            // 根据不同阶段选择不同的访问模式
            if (getOp < PHASE_LENGTH) // 热点访问
            {
                key = genRandomNum() % 5;
            }
            else if (getOp < PHASE_LENGTH * 2) // 大范围随机
            {
                key = genRandomNum() % 1000;
            }
            else if (getOp < PHASE_LENGTH * 3) // 顺序扫描
            {
                key = (getOp - PHASE_LENGTH * 2) % 100;
            }
            else if (getOp < PHASE_LENGTH * 4) // 局部性随机
            {
                int locality = (getOp / 1000) % 10;          // /1000得到一组（一组20个值），%10限定值[0,9]
                key = locality * 20 + (genRandomNum() % 20); // *20得到基础值，再加上随机偏移值
            }
            else // 混合访问
            {
                int randomNum = genRandomNum() % 100;
                if (randomNum < 30)
                {
                    key = genRandomNum() % 5;
                }
                else if (randomNum < 60)
                {
                    key = 5 + (genRandomNum() % 95);
                }
                else
                {
                    key = 100 + (genRandomNum() % 900);
                }
            }

            ++getOperations[i];

            std::string result{};
            if (caches[i]->get(key, result))
            {
                ++hits[i];
            }

            // 随机进行set操作，更新缓存内容
            if (genRandomNum() % 100 < 30) // 30%概率进行set
            {
                std::string value = "new" + std::to_string(key);

                caches[i]->set(key, value);
            }
        }
    }

    printResults("工作负载剧烈变化测试", CAPACITY, getOperations, hits);
}

int main()
{
    testHotDataAccess();
    testCyclicScan();
    testWorkLoadShift();

    return 0;
}